package ru.susu.algebra.partition;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import ru.susu.algebra.pair.Pair;

/**
 * Реализация Partition, основанная на прямом представлении элементов в массиве.
 * Требует много памяти, эксперементально порядка 500 байт, лучше не использовать при больших наборах разбиений.
 * При хранении большого разбиения и выполнения множества операций может быть полезна.
 *
 * @author akargapolov
 * @since: 26.02.2009
 */
public class PlainPartition extends PartitionBase
{
	private static final long serialVersionUID = 1716853421449809607L;

	private ArrayList<Pair<Integer, Integer>> _numbers;
	private HashMap<Integer, Integer> _index;
	private Integer _sum;

	public PlainPartition()
	{
		_numbers = new ArrayList<Pair<Integer, Integer>>(4);
		_index = new HashMap<Integer, Integer>(4);
		_sum = 0;
	}

	public PlainPartition(Collection<Pair<Integer, Integer>> numbers)
	{
		this();
		_numbers.addAll(numbers);
		Collections.sort(_numbers);
		for (int index = 0; index < _numbers.size(); index++)
		{
			Pair<Integer, Integer> p = _numbers.get(index);
			_index.put(p.getKey(), index);
			_sum += p.getKey() * p.getValue();
		}
	}

	public PlainPartition(Integer[] array)
	{
		this();
		Arrays.sort(array);
		for (Integer number : array)
			this.add(number);
	}

	public int getPairsLength()
	{
		return _numbers.size();
	}

	public Integer getLength()
	{
		int res = 0;
		for (Pair<Integer, Integer> pair : _numbers)
			res += pair.getValue();
		return res;
	}

	@Override
	public Partition clone()
	{
		PlainPartition res = new PlainPartition();
		res._sum = _sum;
		res._index = new HashMap<Integer, Integer>(_index.size());
		for (Map.Entry<Integer, Integer> entry : _index.entrySet())
			res._index.put(entry.getKey(), entry.getValue());
		res._numbers = new ArrayList<Pair<Integer, Integer>>(_numbers.size());
		for (Pair<Integer, Integer> pair : _numbers)
		{
			Pair<Integer, Integer> newPair = new Pair<Integer, Integer>(pair.getKey(), pair
					.getValue());
			res._numbers.add(newPair);
		}
		return res;
	}

	public Pair<Integer, Integer> getPair(int index)
	{
		return _numbers.get(index);
	}

	public Integer getPresentedNumber()
	{
		return _sum;
	}

	public boolean add(Integer number, Integer count)
	{
		if (_index.containsKey(number))
		{
			int index = _index.get(number);
			Pair<Integer, Integer> pair = _numbers.get(index);
			pair.setValue(pair.getValue() + count);
		}
		else
		{
			_numbers.add(new Pair<Integer, Integer>(number, count));
			int i = _numbers.size() - 1;

			while (i > 0 && _numbers.get(i - 1).compareTo(_numbers.get(i)) > 0)
			{
				Collections.swap(_numbers, i - 1, i);
				--i;
			}
			_index.put(number, i);
		}
		_sum += number * count;
		return true;
	}

	public boolean remove(Integer number, Integer count)
	{
		if (!_index.containsKey(number))
			return false;

		int index = _index.get(number);
		Integer value = _numbers.get(index).getValue();

		_sum -= number * Math.min(value, count);

		if (value > count)
			_numbers.get(index).setValue(value - count);
		else
		{
			_numbers.remove(index);
			_index.remove(number);
		}
		return true;
	}

	@Override
	public int hashCode()
	{
		return this.getOrderNumber();
	}

	@Override
	public boolean equals(Object obj)
	{
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (this.getClass() != obj.getClass())
			return false;
		PlainPartition other = (PlainPartition) obj;
		if (_numbers == null)
		{
			if (other._numbers != null)
				return false;
		}
		else if (!_numbers.equals(other._numbers))
			return false;
		if (_sum == null)
		{
			if (other._sum != null)
				return false;
		}
		else if (!_sum.equals(other._sum))
			return false;
		return true;
	}

	public Integer getOrderNumber()
	{
		return PartitionEnumerator.getNumber(this).intValue();
		// return PartitionsFacade.getOrderNumber(this);
	}

	public Integer[] getArrayRepresentation()
	{
		int length = this.getLength();
		Integer[] array = new Integer[length];
		int index = 0;
		for (int i = 0; i < this.getPairsLength(); i++)
		{
			Pair<Integer, Integer> pair = this.getPair(i);
			for (int j = 0; j < pair.getValue(); j++)
				array[index++] = pair.getKey();
		}
		return array;
	}



}
